class Order:

    def __init__(self, order_type, side, price, quantity):
        self.type = order_type
        self.side = side.lower()
        self.price = price
        self.quantity = quantity


class Trade:

    def __init__(self, price, quantity):
        self.price = price
        self.quantity = quantity


class OrderBook:

    def __init__(self, bids=[], asks=[]):

        self.bids = sorted(bids, key=lambda order: -order.price)
        self.asks = sorted(asks, key=lambda order: order.price)

    def __len__(self):
        return len(self.bids) + len(self.asks)

    def add(self, order):
        if order.type == 'buy':
            self.bids.append(order)
        elif order.type == 'sell':
            self.asks.append(order)

    def remove(self, order):
        if order.type == 'buy':
            self.bids.remove(order)
        elif order.type == 'sell':
            self.asks.remove(order)


from threading import Thread
from collections import deque


class MatchingEngine:

    def __init__(self, threaded=False):

        order1 = Order(order_type="buy", side="buy", price=10, quantity=10)
        order2 = Order(order_type="sell", side="sell", price=10, quantity=20)

        self.queue = deque()
        self.orderbook = OrderBook()

        self.orderbook.add(order1)
        self.orderbook.add(order2)

        self.queue.append(order1)
        self.queue.append(order2)

        self.trades = deque()
        self.threaded = threaded
        if self.threaded:
            self.thread = Thread(target=self.run)
            self.thread.start()

    def run(self):

        while True:
            if len(self.queue) > 0:
                order = self.queue.popleft()
                self.match(order)
                print(self.get_trades())
                print(len(self.orderbook))

    def process(self, order):
        if self.threaded:
            self.queue.append(order)
        else:
            self.match(order)

    def get_trades(self):
        trades = list(self.trades)
        return trades

    def match(self, order):
        if order.side == 'buy':

            filled = 0
            consumed_asks = []

            for i in range(len(self.orderbook.asks)):
                ask = self.orderbook.asks[i]

                if ask.price > order.price:
                    break  # 卖价过高
                elif filled == order.quantity:
                    break  # 已经匹配

                if filled + ask.quantity <= order.quantity:
                    filled += ask.quantity
                    trade = Trade(ask.price, ask.quantity)
                    self.trades.append(trade)
                    consumed_asks.append(ask)
                elif filled + ask.quantity > order.quantity:
                    volume = order.quantity - filled
                    filled += volume
                    trade = Trade(ask.price, volume)
                    self.trades.append(trade)
                    ask.quantity -= volume

            # 没匹配成功的
            if filled < order.quantity:
                self.orderbook.add(Order("limit", "buy", order.price, order.quantity - filled))

            # 成功匹配的移出订单队列
            for ask in consumed_asks:
                self.orderbook.remove(ask)


        elif order.side == 'sell':

            filled = 0
            consumed_bids = []
            for i in range(len(self.orderbook.bids)):
                bid = self.orderbook.bids[i]

                if bid.price < order.price:
                    break
                if filled == order.quantity:
                    break

                if filled + bid.quantity <= order.quantity:
                    filled += bid.quantity
                    trade = Trade(bid.price, bid.quantity)
                    self.trades.append(trade)
                    consumed_bids.append(bid)
                elif filled + bid.quantity > order.quantity:
                    volume = order.quantity - filled
                    filled += volume
                    trade = Trade(bid.price, volume)
                    self.trades.append(trade)
                    bid.quantity -= volume

            if filled < order.quantity:
                self.orderbook.add(Order("limit", "sell", order.price, order.quantity - filled))

            for bid in consumed_bids:
                self.orderbook.remove(bid)
        else:

            self.orderbook.add(order)


if __name__ == '__main__':
    me = MatchingEngine(threaded=True)

    me.run()
